package com.coder.bike.controller;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;

import com.coder.bike.entity.Customer;
import com.coder.bike.entity.Permission;
import com.coder.bike.entity.Staff;
import com.coder.bike.security.AuthUser;
import com.coder.bike.security.CustomerAuthenticationException;
import com.coder.bike.security.CustomerUserDetailsService;
import com.coder.bike.service.ICustomerService;
import com.coder.bike.service.IStaffService;
import com.coder.bike.utils.*;
import com.coder.bike.vo.ResetPasswordDTO;
import com.coder.bike.vo.RouteVO;
import com.coder.bike.vo.TokenVo;
import com.coder.bike.vo.UserInfoVo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.coder.bike.utils.LoginUtil.authenticateUser;
import static com.coder.bike.utils.LoginUtil.verifySmsCode;

@RestController
@RequestMapping("/bike/auth/")
public class AuthController {

    @Resource
    private RedisUtils redisUtils;
    @Resource
    private ICustomerService iCustomerService;
    @Resource
    private IStaffService iStaffService;
    @Resource
    private CustomerUserDetailsService customerUserDetailsService;


    @PostMapping("/refreshToken")
    public Result refreshToken(HttpServletRequest request) {
        //获取token
        String token = request.getHeader("token");
        if (StrUtil.isEmpty(token)) {
            token = request.getParameter("token");
        }
        //从SecurityContextHolder上下文中获取Authentication
        Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();
        UserDetails userDetails = (UserDetails)
                authentication.getPrincipal();
        //从token中获取用户名
        String username =
                JwtUtils.parseToken(token).getClaim("username").toString();
        String newToken = "";
        if (StrUtil.equals(username, userDetails.getUsername())) {
            Map<String, Object> map = new HashMap<>();
            map.put("username", userDetails.getUsername());
            newToken = JwtUtils.createToken(map);
        } else {
            throw new CustomerAuthenticationException("token数据异常");
        }
        //获取本次token过期时间
        NumberWithFormat claim = (NumberWithFormat)
                JwtUtils.parseToken(newToken).getClaim(JWTPayload.EXPIRES_AT);
        DateTime dateTime = (DateTime) claim.convert(DateTime.class, claim);
        long expireTime = dateTime.getTime();
        //创建一个tokenvo的对象，将新生成的token和过期时间存入
        TokenVo tokenVo = new TokenVo();
        tokenVo.setToken(newToken).setExpireTime(expireTime);
        //清除原有token
        redisUtils.del("token:" + token);
        //将新的token存入redis
        long nowTime = DateTime.now().getTime();
        String newTokenKey = "token:" + newToken;
        redisUtils.set(newTokenKey, newToken, (expireTime - nowTime) / 1000);
        return Result.success(tokenVo).setMessage("刷新token成功");
    }

    /**
     * 获取当前用户的信息
     *
     * 该接口不需要参数，通过SecurityContextHolder上下文获取认证信息。
     * 如果认证信息存在，则根据用户类型（STAFF或CUSTOMER）返回相应的用户信息，
     * 包括ID、名称、头像、介绍、身份标识和角色数组。
     * 如果认证信息不存在，则返回失败结果，提示认证信息为空。
     *
     * @return Result 包含用户信息的Result对象，如果获取失败则包含错误信息
     */
    @GetMapping("/info")
    public Result getUserInfo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return Result.fail().setMessage("认证信息为空");
        }
        System.out.println(authentication.getPrincipal());
        AuthUser authUser = (AuthUser) authentication.getPrincipal();
        Object[] permission;
        Object[] rolesArray;
        String name;
        String username;
        String email;
        String introduction;

        if ("STAFF".equals(authUser.getUserType())) {
            // 员工用户 - 从权限中提取角色信息
            rolesArray = authUser.getAuthorities().stream()
                    .map(GrantedAuthority::getAuthority)
                    .filter(authority -> authority.startsWith("ROLE_"))
                    .toArray();
            permission = authUser.getPermissionList().stream()
                    .filter(Objects::nonNull)
                    .map(Permission::getPermissionCode)
                    .toArray();
            username = authUser.getUsername();
            email = authUser.getEmail() != null ? authUser.getEmail() : "";
            name = authUser.getRealname();
            introduction = authUser.getDeptName() != null ?
                    authUser.getDeptName() + "员工" : "系统员工";
        } else {
            // 客户用户
            rolesArray = new String[]{"ROLE_CUSTOMER"};
            permission = authUser.getPermissionList().stream()
                    .filter(Objects::nonNull)
                    .map(Permission::getPermissionCode)
                    .toArray();
            username = authUser.getUsername();
            email = authUser.getEmail() != null ? authUser.getEmail() : "";
            name = authUser.getName() != null ? authUser.getName() : authUser.getNickname();
            introduction = "共享单车用户";
        }

        UserInfoVo userInfoVo = new UserInfoVo(
                authUser.getId(),
                name,
                username,
                authUser.getAvatar(),
                introduction,
                email,
                permission,
                rolesArray
        );

        return Result.success(userInfoVo).setMessage("获取用户信息成功");
    }

    @GetMapping("/menuList")
    public Result getMenuList() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return Result.fail().setMessage("认证信息为空");
        }

        AuthUser user = (AuthUser) authentication.getPrincipal();
        List<Permission> permissionList = user.getPermissionList();
        // 移除按钮级别的权限(permission_type为2)
        permissionList.removeIf(permission ->
                Objects.equals(permission.getPermissionType(), 2));

        List<RouteVO> routeVOList = RouteTreeUtils.buildRouteTree(permissionList, 0);
        return Result.success(routeVOList).setMessage("获取菜单列表成功");
    }

    @PostMapping("/logout")
    public Result logout(HttpServletRequest request, HttpServletResponse
            response) {
        String token = request.getHeader("token");
        if (StrUtil.isEmpty(token)) {
            token = request.getParameter("token");
        }
        Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            //用户一旦登出系统，则清除redis中的token
            redisUtils.del("token:" + token);
            SecurityContextLogoutHandler handler = new
                    SecurityContextLogoutHandler();
            handler.logout(request, response, authentication);
            return Result.success().setMessage("登出成功");
        }
        return Result.fail().setMessage("登出失败");
    }

    @PostMapping("/resetPasswordByPhone")
    public Result resetPasswordByPhone(@RequestBody ResetPasswordDTO dto) {


        // 1. 验证新密码和确认密码是否一致
        if (!dto.getNewPassword().equals(dto.getConfirmPassword())) {
            return Result.fail().setMessage("两次输入的密码不一致");
        }

        // 2. 检查密码长度
        if (dto.getNewPassword().length() < 6) {
            return Result.fail().setMessage("密码长度不能少于6位");
        }

        // 3. 先检查客户表
        Customer customer = iCustomerService.selectByPhone(dto.getPhone());
        if (customer != null) {
            customer.setPassword(dto.getNewPassword());
            boolean updated = iCustomerService.updateById(customer);
            if (updated) {
                // 清除验证码
                redisUtils.del("sms:code:reset_password:" + dto.getPhone());
                return Result.success().setMessage("客户密码重置成功");
            }
            return Result.fail().setMessage("客户密码重置失败");
        }

        // 4. 检查员工表
        Staff staff = iStaffService.selectByPhone(dto.getPhone());
        if (staff != null) {
            staff.setPassword(dto.getNewPassword());
            boolean updated = iStaffService.updateById(staff);
            if (updated) {
                // 清除验证码
                redisUtils.del("sms:code:reset_password:" + dto.getPhone());
                return Result.success().setMessage("员工密码重置成功");
            }
            return Result.fail().setMessage("员工密码重置失败");
        }

        // 5. 如果都没找到
        return Result.fail().setMessage("该手机号未注册");
    }


    @PostMapping("/loginByPhone")
    public Result loginByPhone(@RequestBody Map<String, String> params) {
        String phone = params.get("phone");
        String code = params.get("code");

        // 1. 验证短信验证码
        Result verifyResult = verifySmsCode(redisUtils, phone, "login", code);
        if (!verifyResult.getSuccess()) {
            return verifyResult;
        }

        // 2. 直接通过手机号认证用户
        return LoginUtil.authenticateUserByPhone(redisUtils, customerUserDetailsService, phone);
    }
}